var tgd = tgd || {};
tgd.Simulations = tgd.Simulations || {};
tgd.Simulations.Zombies = tgd.Simulations.Zombies || {};

tgd.Simulations.Zombies.Physics = function (people, width, height) {

	var _people = people;
	var _width = width;
	var _height = height;

	this.move = function(person, position) {
		var current;
		var addX, addY;

		if ((current = _people[person.ID]).Person !== person) {
			throw new Error("ID inconsitancy.  Likely attempt to tamper.");
		}

		if (position.X > 0 && current.Location.X < _width - 2) {
			addX = 1;
		} else if (position.X < 0 && current.Location.X > 0) {
			addX = -1
		} else {
			addX = 0;
		}

		if (position.Y > 0 && current.Location.Y < _height - 2) {
			addY = 1;
		} else if (position.Y < 0 && current.Location.Y > 0) {
			addY = -1
		} else {
			addY = 0;
		}

		current.Location.X += addX;
		current.Location.Y += addY;
	}

	/*
	 * attack - the weapon object of the attacker
	 * person - the person being attacked { Type, Position (relative to attacker) }
	 */
	this.attack = function(attack, person) {
		var temp;
		var idx = 0;
		var current;

		if ((current = _people[attack.ID]).Person.Attack !== attack) {
			throw new Error("ID inconsitancy.  Likely attempt to tamper.");
		}

		//todo: speed up, use ID somehow
		for (var i = 0; i < _people.length; ++i) {
			temp = _people[i].Person;
			var p = _people[i];
			if (p !== current && p.Person.Type === person.Type && p.Location.X === (current.Location.X + person.Position.X) && p.Location.Y === (current.Location.Y + person.Position.Y)) {
				break;
			}
		}

		temp.damage(current.Person.Attack);
	}

	this.getPeopleInAttackRange = function(brain) {
		var idx = 0;
		var current;
		var inAttackRange = [];

		if ((current = _people[brain.ID]).Person.Brain !== brain) {
			throw new Error("ID inconsitancy.  Likely attempt to tamper.");
		}

		for (var i = 0; i < _people.length; ++i) {
			var p = _people[i];
			if (current !== p && p.Person.State === tgd.Simulations.Zombies.Enums.LifeState.Alive && Math.sqrt(Math.pow(p.Location.X - current.Location.X ,2) + Math.pow(p.Location.Y - current.Location.Y ,2)) < current.Person.Attack.Range) {
				inAttackRange.push(p);
			}
		}

		var retVal = {};
		retVal.Civilians = [];
		retVal.Zombies = [];
		retVal.Hunters = [];

		for (var i = 0; i < inAttackRange.length; ++i) {
			var p = inAttackRange[i];
			switch (p.Person.Type) {
				case tgd.Simulations.Zombies.Enums.HumanType.Civilian : retVal.Civilians.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Civilian, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				case tgd.Simulations.Zombies.Enums.HumanType.Hunter : retVal.Hunters.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Hunter, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				case tgd.Simulations.Zombies.Enums.HumanType.Zombie : retVal.Zombies.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Zombie, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				default: throw new Error("Type not real.");
			}
		}

		return retVal;
	}

	/*
	 * returns : { Walls : { NearWall : bool, North? : { Distance } }, Civilians : [], Zombies : [], Hunters : []}
	 */
	this.getVision = function(vision) {
		var idx = 0;
		var current;
		var nearBy = [];

		if ((current = _people[vision.ID]).Person.Vision !== vision) {
			throw new Error("ID inconsitancy.  Likely attempt to tamper.");
		}

		var retVal = {};
		retVal.Walls = {};
		retVal.Civilians = [];
		retVal.Zombies = [];
		retVal.Hunters = [];

		for (var i = 0; i < _people.length; ++i) {
			var p = _people[i];
			if (current !== p && p.Person.State === tgd.Simulations.Zombies.Enums.LifeState.Alive && Math.sqrt(Math.pow(p.Location.X - current.Location.X ,2) + Math.pow(p.Location.Y - current.Location.Y ,2)) < current.Person.Vision.FieldOfView) {
				nearBy.push(p);
			}
		}

		for (var i = 0; i < nearBy.length; ++i) {
			var p = nearBy[i];
			switch (p.Person.Type) {
				case tgd.Simulations.Zombies.Enums.HumanType.Civilian : retVal.Civilians.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Civilian, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				case tgd.Simulations.Zombies.Enums.HumanType.Hunter : retVal.Hunters.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Hunter, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				case tgd.Simulations.Zombies.Enums.HumanType.Zombie : retVal.Zombies.push( { Type : tgd.Simulations.Zombies.Enums.HumanType.Zombie, Position : { X : p.Location.X - current.Location.X , Y : p.Location.Y - current.Location.Y } } ); break;
				default: throw new Error("Type not real.");
			}
		}
        
        //todo: there may be a lot of off by one issues here.
        retVal.Walls.NearWall = false;
        if (current.Location.X < current.Person.Vision.FieldOfView) {
			retVal.Walls.West = { Distance : current.Location.X };
			retVal.Walls.NearWall = true;
        }
        if (current.Location.X + current.Person.Vision.FieldOfView >= _width) {
			retVal.Walls.East = { Distance : _width - current.Location.X };
			retVal.Walls.NearWall = true;
        }
        if (current.Location.Y < current.Person.Vision.FieldOfView) {
			retVal.Walls.North = { Distance : current.Location.Y };
			retVal.Walls.NearWall = true;
        }
        if (current.Location.Y + current.Person.Vision.FieldOfView >= _height) {
			retVal.Walls.South = { Distance : _height - current.Location.Y };
			retVal.Walls.NearWall = true;
        }

		return retVal;
	}
}
